package com.freedom.sql.jdbc.pool.socket;

import java.net.InetSocketAddress;
import java.net.Socket;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.freedom.sql.jdbc.request.LoginPacket;
import com.freedom.sql.jdbc.request.SetNamesPacket;
import com.freedom.sql.jdbc.response.CommonOkPacket;
import com.freedom.sql.jdbc.response.GreetingPacket;
import com.freedom.sql.jdbc.response.LoginOkPacket;
import com.freedom.sql.jdbc.utils.LoggerUtils;

/**
 * Socket池对象工厂
 * 
 * @author 刘志强 2016年7月2日
 *
 */
public class SocketFactory extends BasePooledObjectFactory<MySocket> {

	private static final Logger logger = LogManager.getLogger(SocketFactory.class);
	private String ip;
	private int port;
	private String username;
	private String password;

	public SocketFactory(String ip, int port, String user, String password) {
		this.ip = ip;
		this.port = port;
		this.username = user;
		this.password = password;
	}

	@Override
	public MySocket create() throws Exception {// 在这里处理登录流程
		LoggerUtils.debug(logger, "SocketFactory.create invoked...");
		try {// 从jedis拷贝而来
			Socket socket = new Socket();
			// ->@wjw_add
			socket.setReuseAddress(true);
			socket.setKeepAlive(true); // Will monitor the TCP connection is
			// valid
			socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
			// ensure timely delivery of data
			socket.setSoLinger(true, 0); // Control calls close () method,
			// the underlying socket is closed
			// immediately
			// <-@wjw_add
			socket.connect(new InetSocketAddress(ip, port), 3000);
			socket.setSoTimeout(3000);
			String salt = new GreetingPacket(socket).read();
			new LoginPacket(socket, 0, this.username, this.password, salt).write();
			new LoginOkPacket(socket).read();
			String charset = "utf8";
			new SetNamesPacket(socket, charset).write();// 发送names-utf8的命令
			CommonOkPacket commonPacket = new CommonOkPacket(socket).read();// 结束
			// 判断成功还是失败
			byte[] allBytes = commonPacket.getAllBytes();
			if (0 != allBytes[0]) {
				LoggerUtils.error(logger, "commons pool set names utf8 fail...");
				throw new Exception("commons pool set names utf8 fail...");
			}
			MySocket mySocket = new MySocket(socket);
			mySocket.setCharset(charset);
			LoggerUtils.info(logger, "xxx-commons pools create socket " + socket + " succeed...");
			return mySocket;
		} catch (Exception e) {
			new Exception().printStackTrace();
			LoggerUtils.error(logger, e.toString());
			throw e;
		}
	}

	@Override
	public PooledObject<MySocket> wrap(MySocket socket) {
		return new DefaultPooledObject<MySocket>(socket);
	}

	@Override
	public PooledObject<MySocket> makeObject() throws Exception {
		return wrap(create());
	}

	@Override
	public void activateObject(PooledObject<MySocket> p) throws Exception {
		// 由于此函数会在多个场合被调用，所以保留empty
	}

	@Override
	public boolean validateObject(PooledObject<MySocket> p) {
		// 校验这个socket是否正确
		MySocket mySocket = p.getObject();
		// LoggerUtils.debug(logger, "socket validate invoked..." +
		// s.toString());
		Socket s = mySocket.getChannel();
		boolean closed = s.isClosed();
		boolean connected = s.isConnected();
		boolean outputShutdown = s.isOutputShutdown();
		boolean inputShutdown = s.isInputShutdown();
		boolean urgentFlag = false;
		try {
			// s.sendUrgentData(0xFF);
			urgentFlag = true;
		} catch (Exception e) {
			LoggerUtils.error(logger, e.toString());
		}
		// 正常探测以及业务侧探测,业务发生异常，也会关闭连接
		return urgentFlag && connected && !closed && !inputShutdown && !outputShutdown
				&& (false == mySocket.isException());
	}

	@Override
	public void passivateObject(PooledObject<MySocket> p) throws Exception {
		// 由于此函数会在多个场合被调用，所以保留empty
	}

	@Override
	public void destroyObject(PooledObject<MySocket> p) throws Exception {
		Socket socket = p.getObject().getChannel();
		if (null != socket) {
			socket.close();
		}
	}

}
